- #References
- #Introduction
In C++ classes and structures are essentially the same thing. They both are going to create an object. The difference is conventional, they can be both used to do same task, but people choose structures to handle smaller data sets. The major difference lies in default access modifiers (public, private, protected(only be used in inheritance)). For structures the default access modifier for members or methods are public and for classes the default access modifiers is private.
Both structures and classes can have instances(objects),constructors and methods to access variables, they both pass by value. Structures are used for small data structures and classes are used to handle large data structures.
Variables, methods, and constructors are termed as members of classes or structures.
- Used for smaller data.
- Private Members can be accessed only within the body of structure definition, whereas public members are accessed by objects / or outside the definition of structures.
-
Structures members are by default
Public
.
#include<iostream> struct person{ std::string name; std::string job; int age; float salary; float get_bank_info() { return Bank_balance; } private: float Bank_balance=15000; }; int main() { person me; me.age=28; me.name="vishal rao"; me.job="Phd Student"; me.salary=37000; std::cout<<"my age is "<<me.age<<std::endl; std::cout<<"bank balance is :"<<me.Bank_balance<<std::endl; //will generate error as we can't access private members of object me, which is instance of structure 'person' std::cout<<"bank info is :"<<me.get_bank_info(); return 0; }
- Used to handle complex tasks, data sets. We can even use structures rather than classes, but conventional approach is to use classes for complex data structure.
- By default members of classes are Private.
#include<iostream> class person{ float Bank_balance=15000; public: std::string name; std::string job; int age; float salary; float get_bank_info() { return Bank_balance; } }; int main() { person me; me.age=28; me.name="vishal rao"; me.job="Phd Student"; me.salary=37000; std::cout<<"my age is "<<me.age<<std::endl; /* std::cout<<"bank balance is :"<<me.Bank_balance<<std::end; //will generate error as we can't access private members of object me, which is instance of structure 'person' */ std::cout<<"bank info is :"<<me.get_bank_info()<<std::endl; return 0; }
- Constructors are used to quickly create instances of classes. It is special method of class, which can be used to create instance quickly. These are methods with same name as of class name but with no return type. Example:
- Constructors allows us to initialise objects with particular values.
- constructors are not members of class, these are special functions/methods to create or initialise the object. Note that constructor is never inherited by child classes.
-
Default constructor is created implicitly if not defined inside the definition of class. Provided that implicitly defined constructor uses absolutely bare minimum required to crate a user. Example: when we don't define any constructor and crate instance of class
person
by using : person p1; then the default implicit constructor person(){ //nothing } will be called. - Default constructor / implicit constructor is only available if we don't define any custom constructor.
-
If we need default constructor provided we have declared custom constructor then we have to define it explicitly along with custom constructor. Example:
classname(){//this is explicitly defined default constructor}
-
These constructors uses parameters in definition of constructor. Example:
class person(){ float Bank_balance=15000; public: string name; string job; int age; float salary; float get_bank_info() { return Bank_balance; } //Below is example of custom constructor which is mostly used. person(string name, string job_name,int age_user, float salary_user ){ cout<<"a custom constructor is called since an object is created using custom constructor\n"; this->name=name; // or we can just use name=name this->job=job_name; // or job=job_name; this->age=age_user; // or age=age_user; this->salary=salary_user; } };
- Note that we are using this-> pointer here, which is useful when we use same name of parameters to that of members of class. Above we are not using the same name, so we can just write : this->name=name; job=job_name; age=age_user; salary=salary_user;
-
We don't need to define custom/default Destructors, as they destroy the object created and it happens automatically, we can also define Destructor explicitly using
~
prefix before name of destructor which have same name to that of class. Example:~ class_name(){cout<<"an object is destroyed\n"}
#include<iostream> using std::cout; using std::cin; using std::endl; using std::string; class person{ float Bank_balance=15000; public: string name; string job; int age; float salary; float get_bank_info() { return Bank_balance; } person(string name, string job_name,int age_user, float salary_user ){ cout<<"a custom constructor is called since an object is created using custom constructor\n"; this->name=name; // or we can just use name=name this->job=job_name; // or job=job_name; this->age=age_user; // or age=age_user; this->salary=salary_user; } //Since we have created our custom constructor(custom constructor takes parameters), we need to create default constructor with no parameters, since it will not be created by compiler once we have created custom constructor. person(){cout<<"a default constructor is called since an object is created with default constructor\n"} ~person(){cout<<"object is destroyed using this destructor \n";} //unnecessary as it is automatically / implicitly created by compiler. }; int main() { //person me; will not work as implicit constructor is only there if we don't define any constructor. person me("vishal rao","jrf",28,37000); cout<<"my age is "<<me.age<<endl; /* std::cout<<"bank balance is :"<<me.Bank_balance<<std::end; //will generate error as we can't access private members of object me, which is instance of structure 'person' */ cout<<"bank info is :"<<me.get_bank_info()<<endl; person p1("naresh rao","software engineer",29,120000); cout<<p1.age<<endl; person yogesh; yogesh.name="Yogesh"; yogesh.age=23; cout<<"name of yogesh is "<<yogesh.name<<endl; cout<<"age of yogesh is "<<yogesh.age<<endl; return 0; }
-
Once we have crated objects, let us see what we can do with them.
#include<iostream> #include<vector> using std::cout; using std::endl; using std::string; using std::vector; class person{ float Bank_balance=15000; public: string name; string job; int age; float salary; float get_bank_info() { return Bank_balance; } person(string name_user, string job_name,int age_user, float salary_user ){ cout<<"a custom constructor is called since an object is created using custom constructor\n"; this->name=name_user; // or we can just use name=name_user this->job=job_name; // or job=job_name; this->age=age_user; // or age=age_user; this->salary=salary_user; } //Since we have created our custom constructor(custom constructor takes parameters), we need to create default constructor with no parameters, since it will not be created by compiler once we have created custom constructor. person(){cout<<"a default constructor is called since an object is created with default constructor\n";} ~person(){cout<<"object is destroyed using this destructor \n";} //unnecessary as it is automatically / implicitly created by compiler. }; void add_to_vector_object_if_not_exit(vector <person> &VectorObject, person obj) { //this function will return the index of VectorObject each time it finds the match with previous records, and will also return the new position of newly added vector if new object to be added is new. bool found=false; for(int i=0;i<VectorObject.size();i++) { if (VectorObject[i].name==obj.name && VectorObject[i].age==obj.age){ cout<<"object already exist at location"<<i<<endl; found=true; break; }} if (!found){ VectorObject.push_back(obj); cout<<"the new object is added to the position"<<VectorObject.size()-1<<endl; } } int main() { //person me; will not work as implicit constructor is only there if we don't define any constructor. person vishal("vishal rao","jrf",28,37000); person naresh("naresh rao","software engineer",29,120000); person yogesh("Yogesh rao","student",22,0); vector<person> VectorObject; VectorObject.push_back(vishal); VectorObject.push_back(naresh); VectorObject.push_back(yogesh); // we can now access the values of this vector using array. cout<<"the name of first object is "<<VectorObject[0].name<<endl; cout<<"the name of second object is "<<VectorObject[1].name<<endl; cout<<"the name of third object is "<<VectorObject[2].name<<endl; person vishal1("vishal","jrf",28,37000); add_to_vector_object_if_not_exit(VectorObject, vishal1); return 0; }
- Data Encapsulation is the way of making the class user friendly and let the user have only the necessary tools / info to interact with class and not the whole class definition.
- It is similar to how much knowledge a driver of car requires to drive a car (starring wheel, gear, brakes, clutch, brake and accelerator), a driver does not need to know inner workings of engine and other stuff.
There are two ways to achieve Encapsulation: 1. Access Modifiers and 2. Getters and setters
- With use of Access modifiers (public, private and protected) we can achieve Encapsulation. Where as getters and setters are methods to interact with private or protected members of class.
- The Getters methods are to access the private/protected members of class. We can print them or access them.
- Setters are methods to set the private / protected members to some value.
-
In order to access these methods(Getters and Setters) we need to declare them
public
. - Within these functions we can add more layers of functionality to make it user friendly.
Example:
In Below program the function get_bank_balance(getter) and set_bank_balance(is setter)
#include<iostream> using std::cout; using std::endl; using std::string; class person{ float Bank_balance=15000; public: string name; string job; int age; float salary; float get_bank_balance() { return Bank_balance; } void set_bank_balance(float balance) { if (balance>=200000){ cout<<"have you forgot to pay the mess+hostel+fee"<<endl; Bank_balance=balance;} else: Bank_balance=balance; } person(string name_user, string job_name,int age_user, float salary_user ){ cout<<"a custom constructor is called since an object is created using custom constructor\n"; this->name=name_user; // or we can just use name=name_user this->job=job_name; // or job=job_name; this->age=age_user; // or age=age_user; this->salary=salary_user; } person(){cout<<"a default constructor is called since an object is created with default constructor\n";} ~person(){cout<<"object is destroyed using this destructor \n";} //unnecessary as it is automatically / implicitly created by compiler. }; int main() { //person me; will not work as implicit constructor is only there if we don't define any constructor. person vishal("vishal rao","jrf",28,37000); vishal.set_bank_balance(300000); cout<<"new bank balance is "<<vishal.get_bank_balance()<<endl; return 0; }
These data members are specific to class and no instance(object) of class can use them. We can define Static variables, and Static Methods for any class. Example:
#include<iostream> using std::cout; using std::endl; using std::string; class person{ float bank_balance=15000; static int user_count; public: string name; string job; int age; float salary; static void get_user_count() { cout<<"total number of users are:"<<user_count<<endl; //each time an object is created we shall increase the count and each time an object is destroyed we decrease the count. // cout<<age; //will throw some error as we can't use non static member inside static member method } float get_bank_balance() { return bank_balance; } person(string name, string job_name,int age_user, float salary_user ){ cout<<"a custom constructor is called since an object is created using custom constructor\n"; this->name=name; // since both names are same, we need this-> pointer job=job_name; // or job=job_name; age=age_user; // or age=age_user; salary=salary_user; user_count++; } person(){ user_count++; cout<<"a default constructor is called since an object is created with default constructor\n";} ~person(){ user_count--; cout<<"object is destroyed using this destructor \n";} //unnecessary as it is automatically / implicitly created by compiler. }; // we need to initialise the user_count and we can only do that outside the definition of class. (since there can be more users before we implement this program) int person::user_count=0; int main() { person user1,user2,user3,user4; user1.get_user_count(); person::get_user_count(); //get_user_count is static member, it can't be called from users. user1.~person(); //user1 gets destroyed. person::get_user_count(); return 0; }
-
We use Operator overloading to redefine the use of Operators, we have so many Operators in c++, example:
- Arithmetic Operators: +, -, x, /, %
- Comparison Operators: ==, >=, <=, <, >
- Insert and extraction Operators: >>, <<
- and many more
-
while we create objects of class, and we let us say add the objects, then we need to define / overload the
+
Operator. -
Syntax is :
return_type operator + (argument with type){}
Such operator overloading is defined inside a class, or can be define outside usingreturn_type classname::operator + (argument with type){}
#include<iostream> using std::endl; using std::cout; class position{ public: int x; int y; position operator+ (position pos){ position new_position; new_position.x=x+pos.x; new_position.y=y+pos.y; return new_position; } }; int main() { position a,b; a.x=10; a.y=15; b.x=3; b.y=5; //we cant define position c=a+b; as operator + is not defined for such objects of type position(user defined data type) //we need to define the operator overloading in public access modifier. position c; c=a+b; cout<<"The x coordinates of c is "<<c.x<<" "<<c.y<<endl; return 0; }
we first need to declare the operator overloading method inside the class, then only we can define it outside the class.
#include<iostream> using std::endl; using std::cout; class position{ public: int x; int y; position operator+ (position pos){ position new_position; new_position.x=x+pos.x; new_position.y=y+pos.y; return new_position; } bool operator==(position pos); }; //let us do the operator overloading definition outside the class, since it has been declared inside once. bool position::operator ==(position pos){ if(pos.x==position::x && pos.y==position::y){ return true; } else{ return false; } } int main() { position a,b; a.x=10; a.y=15; b.x=3; b.y=5; //we cant define position c=a+b; as operator + is not defined for such objects of type position(user defined data type) //we need to define the operator overloading in public access modifier. position c; c=a+b; cout<<"The x coordinates of c is "<<c.x<<" "<<c.y<<endl; if (a==b){ cout<<"a and b are equal"<<endl; } else{ cout<<"a and b are different"<<endl; } return 0; }
#include<iostream> using std::endl; using std::cout; using std::cin; class position{ public: int x; int y; }; //let us do the operator overloading definition outside the class, since it has been declared inside once. std::istream& operator >> (std::istream &input, position &pos){ cout<<"enter x coordinates of this position"<<endl; input>>pos.x; cout<<"enter y coordinates of this position"<<endl; input>>pos.y; cout<<"Thanks, position has been entered"<<endl; return input; } std::ostream& operator << (std::ostream &output, position &pos){ output<<"x coordinates:"<<pos.x<<" Y coordinates:"<<pos.y; return output; } int main() { position a,b; // we would like to take entries from user and print the positions a and b. //cin>>a,b; won't work as cin is object of type std::istream and a is object of type position.we need to define what does >> mean if we like to use this functionality. cin>>a; cin>>b; cout<<a<<endl; cout<<b<<endl; return 0; }
We can define Friend functions in private member list of class and within definition of friend functions we can use private members.
Note: we can not give any parameters to friend function, we have to provide object of type class as parameter to friend function.
#include <iostream> #include <cmath> using std::cout; using std::endl; class position{ public: float x=0; float y=0; friend float get_position(position pos); private: float radius(float xcord, float ycord){ return std::pow((xcord*xcord+ycord*ycord),0.5); } }; float get_position(position pos){ return pos.radius(pos.x,pos.y); // accessing private member radius() to calculate the radius. } int main(){ position a; a.x=3; a.y=4; cout<<"the distance to origin is "<<get_position(a)<<endl; return 0; }
#include<iostream> using std::endl; using std::cout; using std::cin; class position{ public: int x; int y; friend std::ostream& operator << (std::ostream &output, position &pos); private: std::string status="Real"; }; //let us do the operator overloading definition outside the class, since it has been declared inside once. std::istream& operator >> (std::istream &input, position &pos){ cout<<"enter x coordinates of this position"<<endl; input>>pos.x; cout<<"enter y coordinates of this position"<<endl; input>>pos.y; cout<<"Thanks, position has been entered"<<endl; return input; } std::ostream& operator << (std::ostream &output, position &pos){ output<<"x coordinates:"<<pos.x<<" Y coordinates:"<<pos.y<<endl; output<<"these are "<<pos.status<<endl; //using private member as it is a friend function declared in public access of class. return output; } int main() { position a,b; // we would like to take entries from user and print the positions a and b. //cin>>a,b; won't work as cin is object of type std::istream and a is object of type position.we need to define what does >> mean if we like to use this functionality. cin>>a; cin>>b; cout<<a<<endl; cout<<b<<endl; return 0; }
What if we have to write a huge program with so many classes, namespaces and functions, it is always better to write the function definitions, in one file(named as program.cpp) and all variables, data structure(class and structures), and functions declaration in separate file(program.h).
Steps:
- Move all function, members,data structures in header file and name it "headerfile.h". {we have to make sure that we don't define these variables/members twice, to do that we use `#ifndef HEADERFILE_H #define HEADERFILE_H //all function definitions #endif `
- Define all functions (method of class/ methods of structures and any functions) in separate file "program.cpp", inside this file , include header file "headerfile.h".
-
Now write a separate program for int main() and include the header file using
#include "headerfile.h"
Now I am going to do it for above functions and class methods and class members
#ifndef CLASSES_H #define CLASSES_H #include<iostream> #include <ostream> using std::string; class person{ float Bank_balance=15000; public: std::string name; std::string job; int age; float salary; float get_bank_info(); person(std::string name, string job_name,int age_user, float salary_user ); person(); ~person(); }; class position{ public: int x; int y; position operator+ (position pos); bool operator==(position pos); }; std::istream& operator>>(std::istream& input, position pos); std::ostream& operator<<(std::ostream& output, position pos); #endif
definition_program.cpp
#include <iostream> using std::cout; using std::cin; using std::endl; #include "headerfiles/classes.h" /* float person::Bank_balance=15000; */ float person::get_bank_info() { return Bank_balance; } person::person(string name, string job_name,int age_user, float salary_user ){ cout<<"a custom constructor is called since an object is created using custom constructor\n"; this->name=name; this->job=job_name; this->age=age_user; this->salary=salary_user; } person::person(){cout<<"a default constructor is called since an object is created \n";} //defining default constructor as it won't be created by compiler if we have defined our custom constructor with parameters above. person::~person(){cout<<"object is destroyed \n";} //unnecessary as it is automatically / implicitly created by compiler. // position position::operator+ (position pos){ position new_position; new_position.x=x+pos.x; new_position.y=y+pos.y; return new_position; } bool position::operator ==(position pos){ if(pos.x==position::x && pos.y==position::y){ return true; } else{ return false; } } std::istream& operator >> (std::istream &input, position &pos){ cout<<"enter x coordinates of this position"<<endl; input>>pos.x; cout<<"enter y coordinates of this position"<<endl; input>>pos.y; cout<<"Thanks, position has been entered"<<endl; return input; } std::ostream& operator<<(std::ostream& output, position pos){ output<<"x coordinates:"<<pos.x<<" Y coordinates:"<<pos.y<<endl; return output; }
Now compile the program with g++ thisprogramname.cpp definition_program.cpp
#include <iostream> #include "headerfiles/classes.h" using std::cout; using std::endl; int main() { person p1, p2; p1.name="vishal"; p2.name="naresh"; position a,b; a.x=2; a.y=4; b.x=2; b.y=4; cout<<a<<" "<<b<<endl; if (a==b){ cout<<"both positions are same"<<endl; } else{ cout<<"both positions are different"<<endl; } return 0; }
`output:
a default constructor is called since an object is created
a default constructor is called since an object is created
x coordinates:2 Y coordinates:4
x coordinates:2 Y coordinates:4
both positions are same
object is destroyed
object is destroyed
`
Assume that we have created a class for school_members. Now school_members can be teacher and students. We now want to create separate classes for student and teacher and inherit the methods and members of parent class (school_members), we can do so by just using :
class teacher: Public school_members{}
class student: Public school_members{}
Even though we have not defined these classes, we can still create the objects and use the already available public members of Parent class.
`teacher t1,t2;
student s1,s2;
s1.name="vishal";
s2.name="naresh";
t1.age=45;
t2.age=50;
`
#include<iostream> using std::cout; using std::endl; using std::string; class person{ float bank_balance=15000; static int user_count; public: string name; string job; int age; float salary; static void get_user_count() { cout<<"total number of users are:"<<user_count<<endl; //each time an object is created we shall increase the count and each time an object is destroyed we decrease the count. // cout<<age; //will throw some error as we can't use non static member inside static member method } float get_bank_balance() { return bank_balance; } person(string name, string job_name,int age_user, float salary_user ){ cout<<"a custom constructor is called since an object is created using custom constructor\n"; this->name=name; // since both names are same, we need this-> pointer job=job_name; // or job=job_name; age=age_user; // or age=age_user; salary=salary_user; user_count++; } person(){ user_count++; cout<<"a default constructor is called since an object is created with default constructor\n";} ~person(){ user_count--; cout<<"object is destroyed using this destructor \n";} //unnecessary as it is automatically / implicitly created by compiler. }; int person::user_count = 0; // here we can define new child classes called as teacher and student class teacher : public person{ public: teacher(string name, string job_name,int age_user, float salary_user ){ this->name=name; // since both names are same, we need this-> pointer job=job_name; // or job=job_name; age=age_user; // or age=age_user; salary=salary_user; } }; class student : public person{ public: student(string name, string job_name,int age_user, float salary_user ){ this->name=name; // since both names are same, we need this-> pointer job=job_name; // or job=job_name; age=age_user; // or age=age_user; salary=salary_user; } }; int main() { teacher t1("teacher1","teacher",44,230000); person::get_user_count(); student s1("student1","student",16,0000); person::get_user_count(); cout<<"name of teacher1 is "<<t1.name<<endl; cout<<"name of student is "<<s1.name<<endl; return 0; }
`output:
a default constructor is called since an object is created with default constructor
total number of users are:1
a default constructor is called since an object is created with default constructor
total number of users are:2
name of teacher1 is teacher1
name of student is student1
object is destroyed using this destructor
object is destroyed using this destructor
`
-
first we will create the main function
/* #include <iostream> //defined in classes.h so no need */ /* #include <string.h> //defined in classes.h so no need*/ #include "classes.h" /* using std::cout; //defined in classes.h so no need*/ int main(int argc, char *argv[]) { teacher t1; t1.age=25; t1.name="prof. A J"; cout<<t1.name<<" "<<t1.age<<endl; return 0; }
-
Then we shall add the declaration in header file , here in "classes.h"
#ifndef CLASSES_H #define CLASSES_H #include<iostream> #include <ostream> using std::string; using std::endl; using std::cout; class person{ float Bank_balance=15000; public: std::string name; std::string job; int age; float salary; float get_bank_info(); person(std::string name, string job_name,int age_user, float salary_user ); person(); ~person(); }; class position{ public: int x; int y; position operator+ (position pos); bool operator==(position pos); }; class teacher: public person{ public: teacher(); }; class student: public person{ public: student(); }; std::istream& operator>>(std::istream& input, position pos); std::ostream& operator<<(std::ostream& output, position pos); #endif
-
Then we shall define these functions in classes_across_files.cpp
#include <iostream> using std::cout; using std::endl; #include "classes.h" /* float person::Bank_balance=15000; */ float person::get_bank_info() { return Bank_balance; } person::person(string name, string job_name,int age_user, float salary_user ){ cout<<"a custom constructor is called since an object is created using custom constructor\n"; this->name=name; this->job=job_name; this->age=age_user; this->salary=salary_user; } person::person(){cout<<"a default constructor is called since an object is created \n";} //defining default constructor as it won't be created by compiler if we have defined our custom constructor with parameters above. person::~person(){cout<<"object is destroyed \n";} //unnecessary as it is automatically / implicitly created by compiler. teacher::teacher(){ cout<<"teacher object is created with default constructor"<<endl; } student::student(){ cout<<"student object is created with default constructor"<<endl; } // position position::operator+ (position pos){ position new_position; new_position.x=x+pos.x; new_position.y=y+pos.y; return new_position; } bool position::operator ==(position pos){ if(pos.x==position::x && pos.y==position::y){ return true; } else{ return false; } } std::istream& operator >> (std::istream &input, position &pos){ cout<<"enter x coordinates of this position"<<endl; input>>pos.x; cout<<"enter y coordinates of this position"<<endl; input>>pos.y; cout<<"Thanks, position has been entered"<<endl; return input; } std::ostream& operator<<(std::ostream& output, position pos){ output<<"x coordinates:"<<pos.x<<" Y coordinates:"<<pos.y<<endl; return output; }
-
Then we shall compile the main.cpp program
g++ inheritance_with_header_files.cpp classes_across_files.cpp
`output:
a default constructor is called since an object is created
teacher object is created with default constructor
prof. A J 25
object is destroyed
`
If we define same functions again in child class then which function will be called? , to resolve the conflict we use virtual
keyword before the function which we want to ignore. Functions can be overloaded in case we have different types of arguments, or no arguments at all, but there can be two similar functions with same type of parameters. In that case we need the virtual
keyword before the name of function which we would like to ignore.
It is of two type:
-
Compile type
- function overloading (simply define same name functions with different data types, return types, or no parameters, better to use generic programming or use of templates.)
- operator overloading
-
Run type
- virtual functions
We have looked at function overloading and operator overloading. Now we shall look at virtual functions.
#include<iostream> using std::cout; using std::cin; using std::endl; class base{ public: virtual void fun(){ cout<<"this is function of base class with no parameters"<<endl; } virtual void fun(int a){ cout<< "this is function of base class with some argument "<<endl; } virtual void fun(double b){ cout<<"this is function of base class for double parameters "<<endl; } }; class child: public base{ public: void fun(){ cout<<"this is function of child class and has no arguments"<<endl; } void fun(int a){ cout<< "this is function in child class with some int argument "<<endl; } void fun(double b){ cout<<"this is function called in child class for double parameters "<<endl; } }; int main(){ child c1; //now since c1 is object of child class , it inherits all public methods of base class, so if I call c1.fun() then which function will be called? It will call function of child class. c1.fun(); //what if I define a pointer of base class and store address of child object in it? base *basePtr; basePtr = &c1; basePtr->fun(); //which function will be called now? it will call function of base class, but since we have given the address of child object, it must call function for child class objects.To do that we will put `virtual` keyword in front of function definition of base classes. c1.fun(4); c1.fun(4.3); return 0; }
`output:
this is function of child class and has no arguments
this is function of child class and has no arguments
this is function in child class with some int argument
this is function called in child class for float parameters
`